package com.wn.sk.product.core.controller;

import com.wn.sk.product.core.lock.RedisLock;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.Duration;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author yellow Docter
 * @date 2022 -09-05
 * @desc
 */
@RestController
@RequestMapping("order")
@Slf4j
@Api(tags = "测试锁的问题")
public class OrderController {

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 1、下单扣减库存
     * 1.1 演示超卖现象
     * 1.2 模拟三个线程同时访问/order/add方法，下单
     * 1.3 查看问题：
     * <p>
     * 超卖了?
     * 使用 synchronized 或者  Lock 可以解决
     * <p>
     * 01 synchronized 可以实现同步
     * 那些情况下?
     * <p>
     * 那些情况 synchronized会失效 ?
     * 1.  如果对象是多列
     * 2.  如果是 集群  也会失效
     */
    Lock lock = new ReentrantLock();

    @ApiOperation("买商品,测试")
    @GetMapping("test01")
    public String addOrder() {

//        lock.lock(); //加锁
        try {
            //1. 从redis中获取库存
            Integer stock = Integer.parseInt(redisTemplate.opsForValue().get("goods:100"));
            if (stock > 0) {
                //2. 扣减库存
                stock--;
                //3. 更新redis
                redisTemplate.opsForValue().set("goods:100", String.valueOf(stock));
                //日志提示 扣减库存成功
                log.info("扣减库存成功,剩余：{}", stock);

            } else {
                return "failure";
            }
        } catch (Exception e) {

        }
//        finally {
////            lock.unlock();
//        }
        return "success";
    }

    @ApiOperation("买商品,测试")
    @GetMapping("test02")
    public String addOrder2() {

        //获取锁
        //  set test 10 nx ex 60  所有的redis的名称都有对应的方法
        // setIfAbsent() 对应的是  这个redis的命令
        Boolean isLock = redisTemplate.opsForValue().setIfAbsent("goods:lock:100", "1", Duration.ofSeconds(20));
        if (!isLock){
            log.info("锁获取失败...");
            return "failure";
        }
        try {
            //1. 从redis中获取库存
            Integer stock = Integer.parseInt(redisTemplate.opsForValue().get("goods:100"));
            if (stock > 0) {
                //2. 扣减库存
                stock--;
                //3. 更新redis
                redisTemplate.opsForValue().set("goods:100", String.valueOf(stock));
                //日志提示 扣减库存成功
                log.info("扣减库存成功,剩余：{}", stock);

            } else {
                return "failure";
            }
        }finally {
            //释放锁
            redisTemplate.delete("goods:lock:100");
        }
        return "success";
    }

    @Autowired
    private RedisLock redisLock;

    @ApiOperation("买商品,测试")
    @GetMapping("test03")
    public String addOrder3() {

        //获取锁
        //  set test 10 nx ex 60  所有的redis的名称都有对应的方法
        // setIfAbsent() 对应的是  这个redis的命令
        Boolean isLock = redisLock.tryLock("goods:lock:100", TimeUnit.SECONDS,20L);
        if (!isLock){
            log.info("锁获取失败...");
            return "failure";
        }
        try {
            //1. 从redis中获取库存
            Integer stock = Integer.parseInt(redisTemplate.opsForValue().get("goods:100"));
            if (stock > 0) {
                //2. 扣减库存
                stock--;
                //3. 更新redis
                redisTemplate.opsForValue().set("goods:100", String.valueOf(stock));
                //日志提示 扣减库存成功
                log.info("扣减库存成功,剩余：{}", stock);

            } else {
                return "failure";
            }
        }finally {
            //释放锁
            redisLock.release("goods:lock:100");
        }
        return "success";
    }

    /**
     *  通过框架
     *   Redisson 解决 分布式锁的问题
     *
     * @return
     */

    @Autowired
    private RedissonClient redissonClient;

    @ApiOperation("买商品,测试")
    @GetMapping("test04")
    public String addOrder4() {
        //获取锁
        RLock rock = redissonClient.getLock("goods:lock:100");
        //加锁

        rock.lock(20L,TimeUnit.SECONDS);
        rock.lock();
        try {
            //1. 从redis中获取库存
            Integer stock = Integer.parseInt(redisTemplate.opsForValue().get("goods:100"));
            if (stock > 0) {
                //2. 扣减库存
                stock--;
                //3. 更新redis
                redisTemplate.opsForValue().set("goods:100", String.valueOf(stock));
                //日志提示 扣减库存成功
                log.info("扣减库存成功,剩余：{}", stock);

            } else {
                return "failure";
            }
        }finally {
            //释放锁
            rock.unlock();
        }
        return "success";
    }
}
